package cn.org.donald.framework.handler;

import cn.org.donald.framework.config.ServiceThreadPool;
import cn.org.donald.framework.constant.InfoConstant;
import cn.org.donald.pojo.entity.system.LogError;
import cn.org.donald.service.system.LogErrorService;
import org.apache.dubbo.config.annotation.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorViewResolver;
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;

/**
 * 全局非controller异常拦截
 * controller的aop无法捕获的异常,用该类进行捕获并写入错误日志数据库
 * @author Donald
 * @since 2020-12-26 23:25:44
 */
@RestControllerAdvice
public class GlobalErrorHandler extends BasicErrorController {

    private Logger logger = LoggerFactory.getLogger(GlobalErrorHandler.class);

    @Reference(version = "1.0.0", group = "donald")
    private LogErrorService logErrorService;

    @Autowired
    private ServiceThreadPool serviceThreadPool;

    public GlobalErrorHandler() {
        super(new DefaultErrorAttributes(),new ErrorProperties());
    }

    public GlobalErrorHandler(ErrorAttributes errorAttributes, ErrorProperties errorProperties, List<ErrorViewResolver> errorViewResolvers) {
        super(errorAttributes, errorProperties, errorViewResolvers);
    }

    public GlobalErrorHandler(ErrorAttributes errorAttributes, ErrorProperties errorProperties) {
        super(errorAttributes, errorProperties);
    }

    @Override
    @RequestMapping(produces = {MediaType.APPLICATION_JSON_VALUE})
    public ResponseEntity<Map<String,Object>> error(HttpServletRequest request) {
        Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));
        Integer code = (Integer) body.get("status");
        String message = body.get("message").toString();

        if (InfoConstant.UNAUTHORIZED.equals(message) || InfoConstant.ANOTHER_DEVICE_LOGIN.equals(message)){
            code = 401;
        } else if (InfoConstant.FORBIDDEN.equals(message)){
            code = 403;
        } else if (code == 404){
            message = InfoConstant.NOT_FOUND;
        } else {
            LogError logError = new LogError();
            logError.setMessage(body.get("message").toString());
            logError.setHttpStatus(code);
            logError.setTipInfo(body.get("error").toString());
            logError.setPath(body.get("path").toString());
            try {
                serviceThreadPool.execute(() -> {
                    boolean flag = logErrorService.add(logError);
                    if (!flag){
                        throw new ServiceException("异常日志写入错误!");
                    }
                });
            } catch (Exception e){
                if (e instanceof ServiceException){
                    logger.error(((ServiceException) e).getInfo());
                } else {
                    logger.error("异常日志写入错误!");
                }
                e.printStackTrace();
            }
            message = InfoConstant.INTERNAL_SERVER_ERROR;
        }
        Map<String,Object> map = new LinkedHashMap<>();
        map.put("code",code);
        map.put("info",message);
        map.put("timestamp",System.currentTimeMillis());
        map.put("data",null);
        return new ResponseEntity<>(map, HttpStatus.OK);
    }
}
